package com.atguigu.ware.service.impl;

import com.atguigu.common.exception.NoStockException;
import com.atguigu.common.to.es.SkuHasStockVo;
import com.atguigu.common.to.mq.OrderTo;
import com.atguigu.common.to.mq.StockDetailTo;
import com.atguigu.common.to.mq.StockLockedTo;
import com.atguigu.common.utils.R;
import com.atguigu.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.ware.entity.WareOrderTaskEntity;
import com.atguigu.ware.enume.OrderStatusEnum;
import com.atguigu.ware.enume.WareTaskStatusEnum;
import com.atguigu.ware.feign.OrderFeignService;
import com.atguigu.ware.feign.ProductFeignService;
import com.atguigu.ware.service.WareOrderTaskDetailService;
import com.atguigu.ware.service.WareOrderTaskService;
import com.atguigu.ware.vo.OrderItemVo;
import com.atguigu.ware.vo.WareSkuLockVo;
import com.atguigu.ware.dao.WareSkuDao;
import lombok.Data;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.ware.entity.WareSkuEntity;
import com.atguigu.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

  @Resource
  WareSkuDao wareSkuDao;
  @Resource
  ProductFeignService productFeignService;
  @Resource
  OrderFeignService orderFeignService;
  @Resource
  WareOrderTaskService wareOrderTaskService;
  @Resource
  WareOrderTaskDetailService wareOrderTaskDetailService;
  @Resource
  RabbitTemplate rabbitTemplate;

  @Override
  public PageUtils queryPage(Map<String, Object> params) {
    IPage<WareSkuEntity> page = this.page(
      new Query<WareSkuEntity>().getPage(params),
      new QueryWrapper<WareSkuEntity>()
    );

    return new PageUtils(page);
  }

  @Override
  public void addStock(Long skuId, Long wareId, Integer skuNum) {
    //1、判断如果还没有这个库存记录新增
    List<WareSkuEntity> entities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
    if (entities == null || entities.size() == 0) {
      WareSkuEntity skuEntity = new WareSkuEntity();
      skuEntity.setSkuId(skuId);
      skuEntity.setStock(skuNum);
      skuEntity.setWareId(wareId);
      skuEntity.setStockLocked(0);
      //TODO 远程查询sku的名字，如果失败，整个事务无需回滚
      //1、自己catch异常
      //TODO 还可以用什么办法让异常出现以后不回滚？高级
      try {
        R info = productFeignService.info(skuId);
        Map<String, Object> data = (Map<String, Object>) info.get("skuInfo");

        if (info.getCode() == 0) {
          skuEntity.setSkuName((String) data.get("skuName"));
        }
      } catch (Exception e) {
      }
      wareSkuDao.insert(skuEntity);
    } else {
      wareSkuDao.addStock(skuId, wareId, skuNum);
    }

  }

  @Override
  public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {
    List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {
      SkuHasStockVo vo = new SkuHasStockVo();
      //查询当前sku总库存量
      Long count = baseMapper.getSkuStock(skuId);
      vo.setSkuId(skuId);
      vo.setHasStock(count != null && count > 0);
      return vo;
    }).collect(Collectors.toList());
    return collect;
  }

  @Transactional
  @Override
  public Boolean orderLockStock(WareSkuLockVo wareSkuLockVo) {

    //因为可能出现订单回滚后，库存锁定不回滚的情况，但订单已经回滚，得不到库存锁定信息，因此要有库存工作单
    WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
    taskEntity.setOrderSn(wareSkuLockVo.getOrderSn());
    taskEntity.setCreateTime(new Date());
    wareOrderTaskService.save(taskEntity);

    List<OrderItemVo> itemVos = wareSkuLockVo.getLocks();
    List<SkuLockVo> lockVos = itemVos.stream().map((item) -> {
      SkuLockVo skuLockVo = new SkuLockVo();
      skuLockVo.setSkuId(item.getSkuId());
      skuLockVo.setNum(item.getCount());
      //找出所有库存大于商品数的仓库
      List<Long> wareIds = baseMapper.listWareIdsHasStock(item.getSkuId(), item.getCount());
      skuLockVo.setWareIds(wareIds);
      return skuLockVo;
    }).collect(Collectors.toList());

    for (SkuLockVo lockVo : lockVos) {
      boolean lock = true;
      Long skuId = lockVo.getSkuId();
      List<Long> wareIds = lockVo.getWareIds();
      //如果没有满足条件的仓库，抛出异常
      if (wareIds == null || wareIds.size() == 0) {
        throw new NoStockException(skuId);
      } else {
        for (Long wareId : wareIds) {
          Long count = baseMapper.lockWareSku(skuId, lockVo.getNum(), wareId);
          if (count == 0) {
            lock = false;
          } else {
            //锁定成功，保存工作单详情
            WareOrderTaskDetailEntity detailEntity = WareOrderTaskDetailEntity.builder()
              .skuId(skuId)
              .skuName("")
              .skuNum(lockVo.getNum())
              .taskId(taskEntity.getId())
              .wareId(wareId)
              .lockStatus(1).build();
            wareOrderTaskDetailService.save(detailEntity);
            //发送库存锁定消息至延迟队列
            StockLockedTo lockedTo = new StockLockedTo();
            lockedTo.setId(taskEntity.getId());
            StockDetailTo detailTo = new StockDetailTo();
            BeanUtils.copyProperties(detailEntity, detailTo);
            lockedTo.setDetailTo(detailTo);
            rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", lockedTo);

            lock = true;
            break;
          }
        }
      }
      if (!lock) throw new NoStockException(skuId);
    }
    return true;
  }


  @Override
  public void unlock(StockLockedTo stockLockedTo) {
    StockDetailTo detailTo = stockLockedTo.getDetailTo();
    WareOrderTaskDetailEntity detailEntity = wareOrderTaskDetailService.getById(detailTo.getId());
    //1.如果工作单详情不为空，说明该库存锁定成功
    if (detailEntity != null) {
      WareOrderTaskEntity taskEntity = wareOrderTaskService.getById(stockLockedTo.getId());
      R<OrderTo> r = orderFeignService.infoByOrderSn(taskEntity.getOrderSn());
      if (r.getCode() == 0) {
        OrderTo order = r.getData();
        //没有这个订单||订单状态已经取消 解锁库存
        if (order == null || Objects.equals(order.getStatus(), OrderStatusEnum.CANCLED.getCode())) {
          //为保证幂等性，只有当工作单详情处于被锁定的情况下才进行解锁
          if (Objects.equals(detailEntity.getLockStatus(), WareTaskStatusEnum.Locked.getCode())) {
            unlockStock(detailTo.getSkuId(), detailTo.getSkuNum(), detailTo.getWareId(), detailEntity.getId());
          }
        }
      } else {
        throw new RuntimeException("远程调用订单服务失败");
      }
    } else {
      //无需解锁
    }
  }

  @Override
  public void unlock(OrderTo orderTo) {
    //为防止重复解锁，需要重新查询工作单
    String orderSn = orderTo.getOrderSn();
    WareOrderTaskEntity taskEntity = wareOrderTaskService.getBaseMapper().selectOne((new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn)));
    //查询出当前订单相关的且处于锁定状态的工作单详情
    List<WareOrderTaskDetailEntity> lockDetails = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", taskEntity.getId()).eq("lock_status", WareTaskStatusEnum.Locked.getCode()));
    for (WareOrderTaskDetailEntity lockDetail : lockDetails) {
      unlockStock(lockDetail.getSkuId(), lockDetail.getSkuNum(), lockDetail.getWareId(), lockDetail.getId());
    }
  }

  private void unlockStock(Long skuId, Integer skuNum, Long wareId, Long detailId) {
    //数据库中解锁库存数据
    baseMapper.unlockStock(skuId, skuNum, wareId);
    //更新库存工作单详情的状态
    WareOrderTaskDetailEntity detail = WareOrderTaskDetailEntity.builder()
      .id(detailId)
      .lockStatus(2).build();
    wareOrderTaskDetailService.updateById(detail);
  }

  /**
   * 库存自动解锁
   *
   * @param to
   * @param message
   */
  @RabbitHandler
  public void handleStockLockedRelease(StockLockedTo to, Message message) {
    System.out.println("收到解锁库存的消息");
    Long id = to.getId();
    StockDetailTo detail = to.getDetailTo();
    Long detailSkuId = detail.getSkuId();
    WareOrderTaskDetailEntity byId = wareOrderTaskDetailService.getById(detailSkuId);
    if (byId != null) {
      //解锁
    } else {
      //无需解锁
    }
  }


  @Data
  class SkuLockVo {
    private Long skuId;
    private Integer num;
    private List<Long> wareIds;
  }
}
